<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2020/7/20
 * Time: 17:06
 */

namespace app\service;


use Redis\Redis;
use think\Db;

class MemberCode
{
    /**
     * @param string $p_code 推荐人ID
     * @param string $team_id 团队ID
     * @return string
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * @conten 生成用户ID  2位字母6位数字
     */
    function getMemberCode($p_code = '', $team_id = '')
    {
// 注释掉的部分是 字母随机
        /*        $t = '';
                for ($i = 0; $i < $prefixLength; $i++) {
                    $salt = mt_rand(1000, 9999);
                    if ($salt % 2) {
                        $t .= chr(mt_rand(97, 122));
                    } else {
                        $t .= chr(mt_rand(65, 90));
                    }
                }

                $code = strtoupper($t) . mt_rand($min, $max);*/
        $letterInfo = MemberCode::getLetter($p_code, $team_id);
        $letter = $letterInfo['code'];
        $team_id = $letterInfo['team_id'];
        $redis = new Redis();
        $memberCode = $redis->hGetJson('leegame', 'memberCode');
        if (empty($memberCode)) {
            $memberCode = Db::table('member')->field('code')->select();
            $memberCode = array_column($memberCode, 'code');
        }
        a:
        $number = MemberCode::getRandomNum(6);
        $code = $letter . $number;
        if (in_array($code, $memberCode)) {
            goto a;
        } else {
            array_push($memberCode, $code);
            $redis->hSetJson('leegame', 'memberCode', $memberCode);
            if(!$letterInfo['mark']){
                # 没有团队,没有p_code
                $nothing = $redis->hGetJson('memberCode','nothing');
                if(empty($nothing)){
                    $nothing=array();
                }
                array_push($nothing,$code);
                $redis->hSetJson('memberCode','nothing',$nothing);
            }
        }
        return array('code' => $code, 'team_id' => $team_id);
    }

    /**
     * @param string $p_code
     * @param string $team_id
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * @content 生成字母部分
     */
    function getLetter($p_code = '', $team_id = '')
    {
        if (!empty($p_code)) {
            # 填写了推荐人(第一个字母和推荐人一样:团队)(第二字母是推荐人顺序加一 A->B)
            preg_match_all('/\p{L}/u', $p_code, $matches, PREG_OFFSET_CAPTURE);
            $lastMatch = $matches[0][count($matches[0]) - 1];
            $teamCode = substr($p_code, 0, $lastMatch[1]);
            $c = substr($p_code, $lastMatch[1] + 1, strlen($p_code) - 6 - strlen($teamCode) - 1);
            $lastMatch[0]++;
            if ($lastMatch[0] == 'AA') {
                $lastMatch[0] = 'A';
                $c = intval($c) + 1;
            }
            # 根据team_code 查询team_id
            $team_id = Db::table('member')->field('team_id')->where(array('code' => $p_code))->find()['team_id'];
//            $team_id = Db::table('team')->field('id')->where(array('team_code' => $teamCode))->find()['id'];
            return array('code' => trim($teamCode . $lastMatch[0] . $c), 'team_id' => $team_id);
        } else {
            $redis = new Redis();
            $mark=true;
            # 未填写推荐人,判断是否填写团队
            if (!empty($team_id)) {
                # 填写了团队,第一个字母是团队 , 第二个字母是顺序
                $teamInfo = Db::table('team')->field('team_code,team_num')->where(array('id' => $team_id))->select();
            } else {
                # 没填团队 , 第一个字母是 人数第二多的团队 , 第二个字母顺序,随机
                $teamInfo = Db::table('team')->field('id,team_code,team_num')
                    ->where(array('is_fake' => 1))
                    ->group('team_num')
                    ->order(array('team_num' => 'desc'))
                    ->limit(0, 2)
                    ->select();
            }
            if(!empty($teamInfo)) {
                if(empty($teamInfo[1])) {
//                    goto nothing;
//                    $teamCode = $teamInfo[0]['team_code'];
                    $teamCode = $teamInfo[0]['team_code'];
                    $team_id = $teamInfo[0]['id'];
                    $remainder = $teamInfo[0]['team_num'] % 26;//余数
                    $multiple = ($teamInfo[0]['team_num'] - $remainder) / 26;//倍数
                    if($multiple==0){
                        $order = LetterToNumber($remainder);
                    }else{
                        $order = LetterToNumber($remainder-1) . $multiple;
                    }
                }else{
                    $teamCode = $teamInfo[1]['team_code'];
                    $team_id = $teamInfo[1]['id'];
                    $remainder = $teamInfo[1]['team_num'] % 26;//余数
                    $multiple = ($teamInfo[1]['team_num'] - $remainder) / 26;//倍数
                    if($multiple==0){
                        $order = LetterToNumber($remainder);
                    }else{
                        $order = LetterToNumber($remainder-1) . $multiple;
                    }
                }
            }else{
                nothing:
                $mark=false;
                $nothing = $redis->hGetJson('memberCode','nothing');
                if(empty($nothing)) {
                    $teamCode = 'A';
                    $team_id = 1;
                    $order='A';
                }else{
                    $lastNothing = end($nothing);
                    preg_match_all('/\p{L}/u', $lastNothing, $matches, PREG_OFFSET_CAPTURE);
                    $lastMatch = $matches[0][count($matches[0]) - 1];
                    $teamCode = substr($lastNothing, 0, $lastMatch[1]);
                    $c = substr($p_code, $lastMatch[1] + 1, strlen($p_code) - 6 - strlen($teamCode) - 1);
                    $teamCode++;
                    if ($teamCode == 'AA') {
                        $teamCode = 'A';
                        $c = intval($c) + 1;
                    }
                    if(!empty($c)){
                        $teamCode  .=$c;
                    }
                    $order='A';
                    $team_id = 1;
                }
            }
            # 第二个字母随机
//            $order = chr(mt_rand(65, 90));
            return array('code' => trim($teamCode . $order), 'team_id' => $team_id,'mark'=>$mark);
        }
    }

    /**
     * @param int $suffixLength
     * @return int
     * @content 生成随机数字
     */
    function getRandomNum($suffixLength = 6)
    {
        $min = intval(str_pad('1', $suffixLength, '0', STR_PAD_RIGHT));
        $max = intval(str_repeat('9', $suffixLength));
        return mt_rand($min, $max);
    }

    /**
     * @param $string
     * @param string $operation
     * @param string $key
     * @param int $expiry
     * @return bool|string
     * @content 生成token(玩家ID  加密/解密)
     */
    function authCode($string, $operation = 'DECODE', $key = '', $expiry = 0)
    {
        // 动态密匙长度，相同的明文会生成不同密文就是依靠动态密匙
        $ckey_length = 4;
        // 密匙
        $key = md5($key ? $key : config('leegame.token_prefix'));

        // 密匙a会参与加解密
        $keya = md5(substr($key, 0, 16));
        // 密匙b会用来做数据完整性验证
        $keyb = md5(substr($key, 16, 16));
        // 密匙c用于变化生成的密文
        $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
        // 参与运算的密匙
        $cryptkey = $keya . md5($keya . $keyc);
        $key_length = strlen($cryptkey);
        // 明文，前10位用来保存时间戳，解密时验证数据有效性，10到26位用来保存$keyb(密匙b)，
        //解密时会通过这个密匙验证数据完整性
        // 如果是解码的话，会从第$ckey_length位开始，因为密文前$ckey_length位保存 动态密匙，以保证解密正确
        $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
        $string_length = strlen($string);
        $result = '';
        $box = range(0, 255);
        $rndkey = array();
        // 产生密匙簿
        for ($i = 0; $i <= 255; $i++) {
            $rndkey[$i] = ord($cryptkey[$i % $key_length]);
        }
        // 用固定的算法，打乱密匙簿，增加随机性，好像很复杂，实际上对并不会增加密文的强度
        for ($j = $i = 0; $i < 256; $i++) {
            $j = ($j + $box[$i] + $rndkey[$i]) % 256;
            $tmp = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }
        // 核心加解密部分
        for ($a = $j = $i = 0; $i < $string_length; $i++) {
            $a = ($a + 1) % 256;
            $j = ($j + $box[$a]) % 256;
            $tmp = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            // 从密匙簿得出密匙进行异或，再转成字符
            $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
        }
        if ($operation == 'DECODE') {
            // 验证数据有效性，请看未加密明文的格式
            if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
                return substr($result, 26);
            } else {
                return '';
            }
        } else {
            // 把动态密匙保存在密文里，这也是为什么同样的明文，生产不同密文后能解密的原因
            // 因为加密后的密文可能是一些特殊字符，复制过程可能会丢失，所以用base64编码
            return $keyc . str_replace('=', '', base64_encode($result));
        }
    }
}